﻿
//Copyright 1997-2009 Syrinx Development, Inc.
//This file is part of the Syrinx Web Application Framework (SWAF).
// == BEGIN LICENSE ==
//
// Licensed under the terms of any of the following licenses at your
// choice:
//
//  - GNU General Public License Version 3 or later (the "GPL")
//    http://www.gnu.org/licenses/gpl.html
//
//  - GNU Lesser General Public License Version 3 or later (the "LGPL")
//    http://www.gnu.org/licenses/lgpl.html
//
//  - Mozilla Public License Version 1.1 or later (the "MPL")
//    http://www.mozilla.org/MPL/MPL-1.1.html
//
// == END LICENSE ==

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Security.Permissions;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;


[assembly: WebResource("ScsAspNet.Syrinx.Gui.AspNet.script.Menu.js", "application/x-javascript")]
namespace Syrinx.Gui.AspNet
{
	[AspNetHostingPermission(SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Minimal)]
	[Themeable(true)]
	[ParseChildren(true, "Items")]
	public class Menu : UserControl
	{
		public enum SubMenuShowEffect { display, fade, slide, toggle };

		protected Orientation m_orient = Orientation.Horizontal;
		protected SubMenuShowEffect m_subMenuShowEffect = SubMenuShowEffect.display;
		protected int m_numLayers, m_itemNumLayers, m_subMenuNumLayers, m_subMenuItemNumLayers;
		protected string m_className, m_itemClassName, m_subMenuClassName, m_subMenuItemClassName;
		protected List<MenuItem> m_items = new List<MenuItem>();
		protected string m_externalLinkDefaultTarget;
        protected string m_currentPageBaseUrl;

		protected override void OnPreRender(EventArgs e)
		{
			base.OnPreRender(e);
			string scriptUrl = Page.ClientScript.GetWebResourceUrl(typeof(Menu), "ScsAspNet.Syrinx.Gui.AspNet.script.Menu.js");
			Page.ClientScript.RegisterClientScriptInclude("Swaf.MenuScript", scriptUrl);
		}

        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
            onLoadMenuSetup(Items);
        }

        protected virtual void onLoadMenuSetup(List<MenuItem> items)
        {
            foreach (MenuItem mi in items)
            {
                mi.onLoadMenuSetup(this);
                if(mi.Items.Count > 0)
                    onLoadMenuSetup(mi.Items);
            }
        }

		[Category("Appearance")]
		public Orientation Orientation { get { return m_orient; } set { m_orient = value; } }

		[Category("Appearance")]
		public SubMenuShowEffect MenuShowEffect { get { return m_subMenuShowEffect; } set { m_subMenuShowEffect = value; } }

		public List<MenuItem> Items { get { return m_items; } }

		[Category("Appearance")]
		public int NumLayers { get { return m_numLayers; } set { m_numLayers = value; } }

		[Category("Appearance")]
		public int ItemNumLayers { get { return m_itemNumLayers; } set { m_itemNumLayers = value; } }

		[Category("Appearance")]
		public int SubMenuNumLayers { get { return m_subMenuNumLayers; } set { m_subMenuNumLayers = value; } }
	
		[Category("Appearance")]
		public int SubMenuItemNumLayers { get { return m_subMenuItemNumLayers; } set { m_subMenuItemNumLayers = value; } }


		[Category("Appearance")]
		public string CssClass { get { return m_className; } set { m_className = value; } }

		[Category("Appearance")]
		public string ItemCssClass { get { return m_itemClassName; } set { m_itemClassName = value; } }

		[Category("Appearance")]
		public string SubMenuCssClass { get { return m_subMenuClassName; } set { m_subMenuClassName = value; } }

		[Category("Appearance")]
		public string SubMenuItemCssClass { get { return m_subMenuItemClassName; } set { m_subMenuItemClassName = value; } }

		public string ExternalLinkDefaultTarget { get { return m_externalLinkDefaultTarget; } set { m_externalLinkDefaultTarget = value; } }

		protected override void Render(HtmlTextWriter writer)
		{
			renderMenuArea(writer, Items, Orientation, NumLayers, CssClass, ItemNumLayers, ItemCssClass, ClientID);
		}

		protected virtual void renderMenuArea(HtmlTextWriter writer, List<MenuItem> items, Orientation orient, int numLayers, string cssClass, int itemNumLayers, string itemCssClass, string firstDivId)
		{
			renderStartLayers(writer, numLayers, cssClass, null, firstDivId);
			writer.Write("<table border='0' cellspacing='0' cellpadding='0'>");
			if (orient == Orientation.Horizontal)
				writer.Write("<tr>");
			bool includeImgSpan = shouldIncludeImageSpanInSubmenu(items);

            renderMenuItems(writer, items, orient, numLayers, cssClass, itemNumLayers, itemCssClass, includeImgSpan);

            if (orient == Orientation.Horizontal)
				writer.Write("</tr>");
			writer.Write("</table>");
			renderEndLayers(writer, numLayers);
		}

        protected virtual void renderMenuItems(HtmlTextWriter writer, List<MenuItem> items, Orientation orient, int numLayers, string cssClass, int itemNumLayers, string itemCssClass, bool includeImgSpan)
        {
            for (int pos = 0; pos < items.Count; pos++)
            {
                MenuItem mi = items[pos];
                mi.setupMenuItem(this);
                if (mi.Visible)
                {
                    if (orient == Orientation.Vertical)
                        writer.Write("<tr>");
                    writer.Write("<td>");
                    if (mi.Items.Count != 0)
                        writer.Write("<div class='{0} SubMenuParent' style='width:100%;'>", orient == Orientation.Horizontal ? "VertMenu" : "HorzMenu");
                    string extraClass = "";
                    if (pos == 0)
                        if (items.Count == 1)
                            extraClass = "FirstChild LastChild";
                        else
                            extraClass = "FirstChild";
                    else if (pos == items.Count - 1)
                        extraClass = "LastChild";
                    if (isMenuItemSelectedPage(mi))
                        extraClass += " Selected";
                    if (mi.CssClass != null)
                        extraClass += " " + mi.CssClass;

                    renderStartLayers(writer, itemNumLayers, itemCssClass, extraClass, null);
                    string target = calcTargetName(mi);
                    if (target.Length != 0)
                        target = string.Format(" target='{0}'", target);

                    writer.Write("<a href='{0}'{2} style='display:block;white-space:nowrap;'><div style='white-space:nowrap;cursor:pointer;width:100%;'>{1}</div></a>",
                        mi.getCurrentNavigateUrl() == null ? "javascript:;" : getMenuItemNavUrl(mi), getMenuItemHtml(includeImgSpan, mi), target);

                    renderEndLayers(writer, itemNumLayers);
                    if (mi.Items.Count != 0)
                    {
                        if (mi.ChildLayout == MenuItem.ChildLayoutType.UseSubMenus)
                        {
                            string extraSubMenuClass = "";
                            if (MenuShowEffect != SubMenuShowEffect.display)
                                extraSubMenuClass = MenuShowEffect.ToString() + "Menu";
                            writer.Write("</div><div class='SubMenu {0}' style='z-index:1000;position:absolute;display:none;'>", extraSubMenuClass);
                            renderMenuArea(writer, mi.Items, Orientation.Vertical, SubMenuNumLayers, SubMenuCssClass, SubMenuItemNumLayers, SubMenuItemCssClass, null);
                            writer.Write("</div>");
                        }
                        else if (mi.ChildLayout == MenuItem.ChildLayoutType.AsSiblings)
                        {
                            renderMenuItems(writer, mi.Items, orient, numLayers, cssClass, itemNumLayers, itemCssClass, includeImgSpan);
                        }
                    }
                    writer.Write("<td>");
                    if (orient == Orientation.Vertical)
                        writer.Write("</tr>");
                }
            }
        }

        protected virtual bool isMenuItemSelectedPage(MenuItem mi)
        {
            bool isIt = false;
            string nav = getMenuItemNavUrl(mi);
            if(nav != null)
                isIt = nav.EndsWith(CurrentPageBaseUrl,StringComparison.CurrentCultureIgnoreCase);   

            return isIt;
        }

        protected virtual string CurrentPageBaseUrl
        {
            get
            {
                if (m_currentPageBaseUrl == null)
                {
                    m_currentPageBaseUrl = Page.Request.RawUrl;
                    int i = m_currentPageBaseUrl.IndexOf('?');
                    if (i > 0)
                        m_currentPageBaseUrl = m_currentPageBaseUrl.Substring(0, i);
                }
                return m_currentPageBaseUrl;
            }
        }

		protected virtual bool shouldIncludeImageSpanInSubmenu(List<MenuItem> menu)
		{
			bool should = false;
			foreach(MenuItem mi in menu)
				if (should = mi.ImageUrl != null)
					break;
			return should;
		}

		protected virtual string getMenuItemHtml(bool includeImgSpan, MenuItem mi)
		{
			return mi.getMenuItemHtml(includeImgSpan);
		}

		protected virtual string getMenuItemNavUrl(MenuItem mi)
		{
            string navUrl = mi.getCurrentNavigateUrl();
			return navUrl==null?null:navUrl.Replace("'","\"");
		}

		protected virtual string calcTargetName(MenuItem mi)
		{
			string target = mi.Target;
			if ((target == null || target.Length == 0) &&
				 mi.NavigateUrl != null && mi.NavigateUrl.IndexOf(':') != -1 && ExternalLinkDefaultTarget != null)
				target = ExternalLinkDefaultTarget;
			return target==null?"":target;
		}

		protected static void renderStartLayers(HtmlTextWriter writer, int numLayers, string cssClass, string extraCssClass, string firstDivId)
		{
			string firstClass = cssClass;
			if (extraCssClass != null)
				firstClass += " " + extraCssClass;

			if(firstDivId != null)
				writer.Write("<div id='{1}' class='{0}'>", firstClass, firstDivId);
			else
				writer.Write("<div class='{0}'>", firstClass);

			char p = 'a';
			for (int pos = 0; pos < numLayers - 1; pos++)
				writer.Write("<div class='{0}{1}'>", cssClass, (char)(p + pos));

			if (numLayers > 0)
			{
				writer.Write("<div class='{0}-c' >",  cssClass);
				writer.Write("<div class='{0}-w'></div>", cssClass);
			}
		}

		protected static void renderEndLayers(HtmlTextWriter writer, int numLayers)
		{
			writer.Write("</div>");
			for (int pos = 0; pos < numLayers; pos++)
				writer.Write("</div>");
		}
	}

	[Serializable]
	[ParseChildren(true, "Items")]
    public class MenuItem
	{
        public enum ChildLayoutType { UseSubMenus, AsSiblings, AsSingleSubMenu };

        protected string m_text, m_navigateUrl, m_target, m_imageUrl, m_cssClass;
        protected bool m_mustHaveChildrenToShow = false;
        protected ChildLayoutType m_childLayout = ChildLayoutType.UseSubMenus;
        protected string m_skinId = null;
        protected List<MenuItem> m_items = new List<MenuItem>();

		public MenuItem()
		{
		}
		public MenuItem(string text, string navigateUrl)
		{
			m_text = text;
			m_navigateUrl = navigateUrl;
		}
		public MenuItem(string text, string navigateUrl, string target)
		{
			m_text = text;
			m_navigateUrl = navigateUrl;
			m_target = target;
		}

        public ChildLayoutType ChildLayout { get { return m_childLayout; } set { m_childLayout = value; } }

        public string Text { get { return m_text; } set { m_text = value; } }

		public string Target { get { return m_target; } set { m_target = value; } }

		public string NavigateUrl { get { return m_navigateUrl; } set { m_navigateUrl = value; } }

        public string ImageUrl { get { return m_imageUrl; } set { m_imageUrl = value; } }

        public string CssClass { get { return m_cssClass; } set { m_cssClass = value; } }

        public bool MustHaveVisibleChildrenToBeVisible { get { return m_mustHaveChildrenToShow; } set { m_mustHaveChildrenToShow = value; } }

		public List<MenuItem> Items { get { return m_items; } }

        public virtual bool Visible
        {
            get
            {
                bool visible = true;
                if (MustHaveVisibleChildrenToBeVisible)
                {
                    visible = false;
                    foreach(MenuItem mi in Items)
                        if (mi.Visible)
                        {
                            visible = true;
                            break;
                        }
                }
                return visible;
            }
        }

		public virtual string getCurrentNavigateUrl() { return NavigateUrl; }

		public virtual string getMenuItemHtml(bool includeImgSpan)
		{
			if (ImageUrl == null)
				return includeImgSpan ? "<span class='nimg'></span>" + Text : Text;

			return string.Format("<img style='border:0;vertical-align:middle;' src='{0}' alt=''/>{1}", ImageUrl, Text);
		}

		public virtual void setupMenuItem(Menu parent)
		{
			//Intentionally left blank - Base menu item has not setup.
		}

        public virtual void onLoadMenuSetup(Menu parent)
        {
            //Intentionally left blank - Base menu item has not setup.
        }
	}
}
